package jscinttools.scint.emissionspectrum;
/* aida libraries */
import hep.aida.ITuple;
import hep.aida.IDataPointSet;
import hep.aida.IDataPoint;
import hep.aida.IMeasurement;
import hep.aida.ref.histogram.Measurement;
/* java libraries */
import java.lang.Double;
import java.lang.System;
import jhplot.P1D;
import jhplot.P0D;
//import fr.in2p3.ipnl.utilities.Utilities;

/**
 *  Extrapolate the correction factor due to the emission spectra, at any
 *  temperature, knowing an table of correction factor and their temperature.
 *
 *@author     Marc-Antoine Verdier
 *@created    8 juin 2011
 */
public class EmSpecCorrCalculator{
	private P0D temper;
	private P0D corrFac;
	private P0D corrFacErr;
	private int inf;
	private int sup;
	private double minT;
	private double maxT;
	//private boolean ascend;
	//private boolean descend;

	/**
	 *  Constructor for the EmSpecCorrCalculator object
	 *
	 *@param  temper   The array of temperatures. It must be sorted either increasing or deacreasing direction
	 *@param  corrFac  The array of corresponding correction factors
	 */
	public EmSpecCorrCalculator(double[] temper, double[] corrFac){
		this.temper = new P0D(temper);
		this.corrFac =  new P0D(corrFac);
		this.corrFacErr = null;
		initExtr();
		//ascend();
		//descend();
	}
	
	/**
	 *  Constructor for the EmSpecCorrCalculator object
	 *
	 *@param  temper   The array of temperatures. It must be sorted either increasing or deacreasing direction
	 *@param  corrFac  The array of corresponding correction factors
	 *@param  corrFacErr  The array of corresponding errors on the correction factors
	 */
	public EmSpecCorrCalculator(double[] temper, double[] corrFac, double[] corrFacErr)
	{
		this.temper = new P0D(temper);
		this.corrFac =  new P0D(corrFac);
		this.corrFacErr = null;
		initExtr();
		//ascend();
		//descend();
	}


	/**
	 *  Constructor for the EmSpecCorrCalculator object
	 *
	 *@param  corrFacDPS  The array of temperatures (coordinate 0) and correction
	 *      factors (cordinate 0) inside a Aida DPS. The temperatures must be
	 *      sorted either increasing or deacreasing direction
	 */
	public EmSpecCorrCalculator(IDataPointSet corrFacDPS){
		this.temper = new P0D();
		this.corrFac = new P0D();
		this.corrFacErr = new P0D();
		for (int i = 0; i < corrFacDPS.size(); i++){
			IDataPoint dp = corrFacDPS.point(i);
			IMeasurement mX = dp.coordinate(0);
			this.temper.add(mX.value());
			IMeasurement mY = dp.coordinate(1);
			this.corrFac.add(mY.value());
			this.corrFacErr.add(mY.errorPlus());
		}
		initExtr();
		//ascend();
		//descend();
	}


	/**
	 *  Constructor for the EmSpecCorrCalculator object
	 *
	 *@param  corrFacP1D  The array of temperatures (X) and correction factors (Y)
	 *      inside a jhplot P1D. The temperatures must be sorted either increasing
	 *      or deacreasing direction
	 */
	public EmSpecCorrCalculator(P1D corrFacP1D){
		this.temper = new P0D(corrFacP1D.getArrayX());
		this.corrFac = new P0D(corrFacP1D.getArrayY());
		this.corrFacErr = new P0D(corrFacP1D.getArrayYupper());
		initExtr();
		//ascend();
		//descend();
	}


	///**
	// *  Constructor for the EmSpecCorrCalculator object
	// *
	// *@param  corrFacTup  The array of temperatures (column 0) and correction
	// *      factors (column 1) inside a Aida ITuple. The temperatures must be
	// *      sorted either increasing or deacreasing direction
	// */
	//public EmSpecCorrCalculator(ITuple corrFacTup)
	//{
	//	this.temper = new double[corrFacTup.rows()];
	//	this.corrFac = new double[corrFacTup.rows()];
	//	corrFacTup.start();
	//	for (int i = 0; i < corrFacTup.rows(); i++)
	//	{
	//		corrFacTup.next();
	//		temper[i] = corrFacTup.getDouble(0);
	//		corrFac[i] = corrFacTup.getDouble(1);
	//	}
	//}
	
	private void initExtr(){
		this.minT = this.temper.getMin();
		this.maxT = this.temper.getMax();
		this.inf = this.temper.find(minT);  
		this.sup = this.temper.find(maxT);
	}

	//private void ascend(){
	//	this.ascend = false;
	//	for (int i = 0; i < temper.size() - 1; i++){
	//		if temper.get(i) <= temper.get(i + 1);
	//			this.ascend = true;
	//		else
	//			this.ascend = false;
	//	}
	//}
	//
	//private void descend(){
	//	this.descend = false;
	//	for (int i = 0; i < temper.size() - 1; i++){
	//		if temper.get(i) >= temper.get(i + 1);
	//			this.descend = true;
	//		else
	//			this.descend = false;
	//	}
	//}

	///**
	// *  Calculate the correction factor for a given temperature
	// *
	// *@param  temperature  The temperature in the same unit as the array of
	// *      temperature
	// *@return              The correction factor
	// */
	//public double extrapolCorrFac(double temperature)
	//{
	//	int equal = -1;
	//	//int inf = (int) Utilities.arrayMinMax(temper)[0][1];
	//	//int sup = (int) Utilities.arrayMinMax(temper)[1][1];
	//	if (temperature < minT)
	//		return corrFac.get(inf);
	//	else if (temperature > maxT)
	//		return corrFac.get(sup);
	//	else
	//	{
	//		for (int i = 0; i < temper.length; i++)
	//		{
	//			if (temper[i] < temperature && temper[i] > temper[inf])
	//				inf = i;
	//			else if (temper[i] > temperature && temper[i] < temper[sup])
	//				sup = i;
	//			else if (temper[i] == temperature)
	//				equal = i;
	//		}
	//		if (equal >= 0)
	//			return corrFac[equal];
	//		else
	//			return calcExtrapolVal(inf, sup, temperature);
	//	}
	//}
	
	/**
	 *  Calculate the correction factor for a given temperature
	 *
	 *@param  temperature  The temperature in the same unit as the array of
	 *      temperature
	 *@return              The correction factor
	 */
	public double extrapolCorrFac(double temperature){
		return extrapolCorrFacAndErr(temperature).value();
	}


	///**
	// *  Linear extrapolation of the correction factor
	// *
	// *@param  inf          Description of the Parameter
	// *@param  sup          Description of the Parameter
	// *@param  temperature  Description of the Parameter
	// *@return              Description of the Return Value
	// */
	//private double calcExtrapolVal(int inf, int sup, double temperature)
	//{
	//	if ((sup - inf) == 1 || (sup - inf) == -1)
	//		return ((corrFac[sup] - corrFac[inf]) / (temper[sup] - temper[inf]) * (temperature - temper[sup])) + corrFac[sup];
	//	else
	//	{
	//		System.out.println("Problem with index");
	//		return 0;
	//	}
	//}
	
	public IMeasurement extrapolCorrFacAndErr(double temperature){
		int equal = -1;
		if (temperature < minT)
			return new Measurement(corrFac.get(inf), corrFacErr.get(inf));
		else if (temperature > maxT)
			return new Measurement(corrFac.get(sup), corrFacErr.get(sup));
		else if (temper.contains(temperature))
			return containedVal(temperature);
		else
			return extrapolMixedVal(temperature);
	}
	
	private IMeasurement containedVal(double temperature){
		int index = temper.find(temperature);
		return new Measurement(corrFac.get(index), corrFacErr.get(index));
	}


	///**
	// *  Linear extrapolation of the correction factor
	// *
	// *@param  temperature  Description of the Parameter
	// *@return              Description of the Return Value
	// */
	//private IMeasurement extrapolVal(double temperature){
	//	if (ascend && !descend)
	//		return extrapolAscendVal(temperature);
	//	else if (!ascend && descend)
	//		return extrapolDescendVal(temperature);
	//	else if (!ascend && !descend)
	//		return extrapolMixedVal(temperature);
	//	else
	//		return null;
	//}
	//
	//private IMeasurement extrapolAscendVal(double temperature){
	//	int index = 0;
	//	while (temper.get(index) < temperature)
	//		index++;
	//	return calcExtrapol(index, index + 1, temperature);
	//}
	//
	//private IMeasurement extrapolDescendVal(double temperature){
	//	int index = 0;
	//	while (temper.get(index) > temperature)
	//		index++;
	//	return calcExtrapol(index, index + 1, temperature);
	//}
	
	private IMeasurement extrapolMixedVal(double temperature){
		double valMin = minT;
		double valMax = maxT;
		for (int i = 0; i < temper.size(); i++){
			double t = temper.get(i);
			if (t < temperature && t >= valMin)
				valMin = t;
			if (t > temperature && t <= valMax)
				valMax = t;
		}
		int indLow = temper.find(valMin);
		int indHi = temper.find(valMax);
		return calcExtrapol(indLow, indHi, temperature);
	}
	
	private IMeasurement calcExtrapol(int ind1, int ind2, double temperature){
		double t1 = temper.get(ind1);
		double t2 = temper.get(ind2);
		double cf1 = corrFac.get(ind1);
		double cf2 = corrFac.get(ind2);
		double cfErr1 = corrFacErr.get(ind1);
		double cfErr2 = corrFacErr.get(ind2);
		double p1 = (cf1 - cf2) / (t1 - t2);
		double p0 = cf1 - (p1 * t1);   
		double p1Err = (cfErr1 - cfErr2) / (t1 - t2);
		double p0Err = cfErr1 - (p1Err * t1);
		double extrapolVal = (temperature * p1) + p0;
		double extrapolValErr = (temperature * p1Err) + p0Err;
		return new Measurement(extrapolVal, extrapolValErr);
	}
}

